#pragma once
#include "httpinfo.hpp"

#define DEFAULT_TIMEOUT 10

class HttpServer
{
private:
    using Handler = std::function<void(const HttpRequest&, HttpResponse*)>;
    using Handlers = std::vector<std::pair<std::regex, Handler>>;

private:
    void ErrorHandler(HttpResponse& rsp)
    {
        std::string body = "<html>";
        body += "<head><meta http-equiv='Content-Type' content='text/html;charset=utf-8'></head>";
        body += "<body><h1>" + std::to_string(rsp._statu) + " " + Util::StatuDesc(rsp._statu) + "</h1></body>";
        body += "</html>";

        rsp.SetContent(body, "text/html");
    }

    // 完善http的响应报头，并将http相应解析为字符串发送到客户端(放入发送缓冲区中等待发送)
    void WriteResponse(const PtrConnection& conn, const HttpRequest& req, HttpResponse& rsp)
    {
        if(req.Close() == true)
            rsp.SetHeader("Connection", "close");
        else
            rsp.SetHeader("Connection", "keep-alive");

        if(rsp._body.empty() == false && rsp.HasHeader("Content-Length") == false)
            rsp.SetHeader("Content-Length", std::to_string(rsp._body.size()));

        if(rsp._body.empty() == false && rsp.HasHeader("Content-Type") == false)
            rsp.SetHeader("Content-Type", "application/octet-stream");

        if(rsp._redirect = true)
            rsp.SetHeader("Location", rsp._redirect_url);       

        std::string rsp_str = req._version + " " + std::to_string(rsp._statu) + " " + Util::StatuDesc(rsp._statu) + "\r\n"; 

        for(auto& head : rsp._headers)
            rsp_str += head.first + ": " + head.second + "\r\n"; 
        rsp_str += "\r\n"; 

        rsp_str += rsp._body;

        conn->Send(rsp_str.c_str(), rsp_str.size());
    }

    bool IsFileHandler(const HttpRequest& req)
    {
        if(_base_dir.empty() == true)
            return false;
        if(req._method != "GET" && req._method != "HEAD")
            return false;
        // 请求资源必须处于静态资源目录内
        if(Util::VaildPath(req._path, _base_dir) == false)
            return false;
        std::string req_path = _base_dir + req._path;
        if(req_path.back() == '/')
            req_path += "index.html";
        if(Util::IsRegular(req_path) == false)
            return false;
        return true;
    }

    // 读取所请求的普通文件的内容作为响应报文返回给客户端
    void FileHandler(const HttpRequest& req, HttpResponse* rsp)
    {
        std::string req_path = _base_dir + req._path;
        // 当请求资源为目录时，默认访问index.html文件
        if(req_path.back() == '/')
            req_path += "index.html";
        if(Util::ReadFile(req_path, &(rsp->_body)) == false)
            return ;
        rsp->SetHeader("Content-Type", Util::ExtMime(req_path));
    }
    
    // 在请求方法函数中获取与请求资源相符的函数执行
    void Dispatcher(HttpRequest& req, HttpResponse& rsp, Handlers& handlers)
    {
        for(auto& handler : handlers)
        {
            const std::regex& re = handler.first;
            if(std::regex_match(req._path, req._matches, re))
            {
                const Handler& functor = handler.second;
                return functor(req, &rsp);
            }
        }
        rsp._statu = 404;
    }
    
    // 为不同请求方法匹配不同的处理方式
    void Route(HttpRequest& req, HttpResponse& rsp)
    {
        if(IsFileHandler(req) == true)
            return FileHandler(req, &rsp);

        if(req._method == "GET" || req._method == "HEAD")
            Dispatcher(req, rsp, _get_handlers);
        else if(req._method == "POST")
            Dispatcher(req, rsp, _post_handlers);
        else if(req._method == "PUT")
            Dispatcher(req, rsp, _put_handlers);
        else if(req._method == "DELETE")
            Dispatcher(req, rsp, _delete_handlers);
        else
            rsp._statu = 405; //Method Not Allowed
    }

    void OnConnected(const PtrConnection& conn)
    {
        conn->SetContext(HttpContext());
        logMessage(NORMAL, "new connection : %p", conn.get()); 
    }

    void OnMessage(const PtrConnection& conn, Buffer* buffer)
    {
        // 当接收到客户端的connect方法，进行缓冲区中http请求的解析和http响应的返回
        // 由于可能一次性接受到客户端的多个请求，因此需要循环解析直到缓冲区中的数据解析完毕
        while(buffer->ReadAbleSize() > 0)
        {
            HttpContext* context = conn->GetContext()->GetContent<HttpContext>();

            // 解析http请求
            Stage recv_statu = context->RecvHttpRequest(buffer);
            HttpRequest& req = context->GetRequest();
            HttpResponse rsp(context->GetRespStatu());  

            // 组织http响应
            if(recv_statu == RECV_HTTP_ERROR)
            {
                ErrorHandler(rsp);
                WriteResponse(conn, req, rsp);
                // 清空context的内容，避免错误数据(recv_statu等)对剩余数据的处理(Shutdown)产生影响(接受缓冲区中存在数据会再次调用该函数)
                context->Reset();
                // buffer->Clear();
                conn->Shutdown();
                return ;
            }
            else if(recv_statu != RECV_HTTP_OVER) //请求正文未接受完毕，即接收缓冲区的数据读取完毕也不足请求正文的长度，退出函数等待下次接受剩余的请求正文
            {
                return ;
            }

            // 处理http请求
            Route(req, rsp);
            // 完善http响应并发送回客户端(放到发送缓冲区中)
            WriteResponse(conn, req, rsp);
            // 重置context，以处理下一个客户端的请求
            context->Reset();
            // 短链接则在处理完一次请求后将发送缓冲区的数据发送并关闭服务端与该客户端的连接
            if(rsp.Close() == true)
            {
                // 短链接只处理一次请求，而处理剩余数据时会解析完接受缓冲区的数据，因此需要清除接受缓冲区中可能存在的多次请求
                buffer->clear();
                conn->Shutdown();
                return ;
            }
        }
    }

public:
    HttpServer(int port, int timeout = DEFAULT_TIMEOUT)
    :_server(port)
    {
        _server.SetConnectedCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
        _server.SetMessageCallback(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
        if(timeout > 0)
            _server.EnableInactiveRelease(timeout);
    }
    
    // 设置静态资源根目录
    void SetBaseDir(std::string& base_dir) 
    {
        assert(Util::IsDirectory(base_dir) == true);   
        _base_dir = base_dir; 
    }

    //设置线程池大小
    void SetThreadCount(size_t count)   { _server.SetThreadCount(count); }

    //设置GET方法
    void Get(const std::string& pattern, const Handler& handler)
    {
        _get_handlers.push_back(std::make_pair(std::regex(pattern), handler));
    }
    
    //设置POST方法
    void Post(const std::string& pattern, const Handler& handler)
    {
        _post_handlers.push_back(std::make_pair(std::regex(pattern), handler));
    }

    //设置PUT方法
    void Put(const std::string& pattern, const Handler& handler)
    {
        _put_handlers.push_back(std::make_pair(std::regex(pattern), handler));
    }

    //设置DELETE方法
    void Delete(const std::string& pattern, const Handler& handler)
    {
        _delete_handlers.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void Listen()
    {
        _server.Start();
    }

private:
    Handlers _get_handlers;
    Handlers _post_handlers;
    Handlers _put_handlers;
    Handlers _delete_handlers;
    std::string _base_dir; //静态资源根目录
    TcpServer _server; 
};